// Copyright (C) 1985-1998 by Symantec
// Copyright (C) 2000-2009 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */

#if (SCPP || MARS) && !HTOD

#include	<stdio.h>
#include	<time.h>

#include	"cc.h"
#include	"global.h"
#include	"el.h"
#include	"go.h"
#include	"type.h"
#include	"oper.h"
#include 	"../l_service/c_ds/vec.h"

static char __file__[] = __FILE__;	/* for tassert.h		*/
#include	"tassert.h"

/* Since many routines are nearly identical, we can combine them with	*/
/* this flag:								*/

#define AE	1
#define CP	2
#define VBE	3

static int flowxx;		/* one of the above values		*/

static vec_t ambigsym = NULL;

STATIC void rdgenkill(void);
STATIC void numdefelems(elem *n);
STATIC void asgdefelems(block *b , elem *n);
STATIC void aecpgenkill(void);
STATIC int  numaeelems(elem *n);
STATIC void numcpelems(elem *n);
STATIC void asgexpelems(elem *n);
STATIC void defstarkill(void);
STATIC void rdelem(vec_t *pgen , vec_t *pkill , elem *n);
STATIC void aecpelem(vec_t *pgen , vec_t *pkill , elem *n);
STATIC void accumaecp(vec_t GEN , vec_t KILL , elem *n);
STATIC void accumaecpx(elem *n);
STATIC void lvgenkill(void);
STATIC void lvelem(vec_t *pgen , vec_t *pkill , elem *n);
STATIC void accumlv(vec_t GEN , vec_t KILL , elem *n);
STATIC void accumvbe(vec_t GEN , vec_t KILL , elem *n);
STATIC void accumrd(vec_t GEN , vec_t KILL , elem *n);
STATIC void flowaecp(void);

/***************** REACHING DEFINITIONS *********************/

/************************************
 * Compute reaching definitions (RDs).
 * That is, for each block B and each program variable X
 * find all elems that could be the last elem that defines
 * X along some path to B.
 * Binrd = the set of defs reaching the beginning of B.
 * Boutrd = the set of defs reaching the end of B.
 * Bkillrd = set of defs that are killed by some def in B.
 * Bgenrd = set of defs in B that reach the end of B.
 */

void flowrd()
{	register vec_t tmp;
	register unsigned i;
	register bool anychng;

	rdgenkill();		/* Compute Bgen and Bkill for RDs	*/
	if (deftop == 0)	/* if no definition elems		*/
		return;		/* no analysis to be done		*/

	/* The transfer equation is:					*/
	/*	Bin = union of Bouts of all predecessors of B.		*/
	/*	Bout = (Bin - Bkill) | Bgen				*/
	/* Using Ullman's algorithm:					*/

	for (i = 0; i < dfotop; i++)
		vec_copy(dfo[i]->Boutrd,dfo[i]->Bgen);

	tmp = vec_calloc(deftop);
	do
	{	anychng = FALSE;
		for (i = 0; i < dfotop; i++)	/* for each block	*/
		{	register block *b;
			register list_t bp;

			b = dfo[i];

			/* Binrd = union of Boutrds of all predecessors of b */
			vec_clear(b->Binrd);
			if (b->BC != BCcatch)
			{
			    /* Set Binrd to 0 to account for:
			     * i = 0;
			     * try { i = 1; throw; } catch () { x = i; }
			     */
			    for (bp = b->Bpred; bp; bp = list_next(bp))
				vec_orass(b->Binrd,list_block(bp)->Boutrd);
			}
			/* Bout = (Bin - Bkill) | Bgen */
			vec_sub(tmp,b->Binrd,b->Bkill);
			vec_orass(tmp,b->Bgen);
			if (!anychng)
				anychng = !vec_equal(tmp,b->Boutrd);
			vec_copy(b->Boutrd,tmp);
		}
	} while (anychng);		/* while any changes to Boutrd	*/
	vec_free(tmp);

#if 0
	dbg_printf("Reaching definitions\n");
	for (i = 0; i < dfotop; i++)
	{	register block *b = dfo[i];

		assert(vec_numbits(b->Binrd) == deftop);
		dbg_printf("B%d Bin ",i); vec_println(b->Binrd);
		dbg_printf("  Bgen "); vec_println(b->Bgen);
		dbg_printf(" Bkill "); vec_println(b->Bkill);
		dbg_printf("  Bout "); vec_println(b->Boutrd);
	}
#endif
}

/***************************
 * Compute Bgen and Bkill for RDs.
 */

STATIC void rdgenkill()
{	register unsigned i,deftopsave;

#if TX86
	util_free(defnod);		/* free existing junk		*/
#elif !(TARGET_MAC)	/* might be from previous memory pool */
	MEM_BEF_FREE(defnod);		/* free existing junk		*/
#endif
	defnod = NULL;

	/* Compute number of definition elems. */
	deftop = 0;
	for (i = 0; i < dfotop; i++)
		if (dfo[i]->Belem)
		{
			numdefelems(dfo[i]->Belem);
		}
	if (deftop == 0)
		return;

	/* Allocate array of pointers to all definition elems	*/
	/*	The elems are in dfo order.			*/
	/*	defnod[]s consist of a elem pointer and a pointer */
	/*	to the enclosing block.				*/
#if TX86
	defnod = (dn *) util_calloc(sizeof(dn),deftop);
#else
	defnod = (dn *)MEM_BEF_CALLOC(sizeof(dn) * deftop);
#endif
	deftopsave = deftop;
	deftop = 0;
	for (i = 0; i < dfotop; i++)
		if (dfo[i]->Belem)
			asgdefelems(dfo[i],dfo[i]->Belem);
	assert(deftop == deftopsave);

	for (i = 0; i < dfotop; i++)	/* for each block		*/
	{	register block *b = dfo[i];

		/* dump any existing vectors */
		vec_free(b->Bgen);
		vec_free(b->Bkill);
		vec_free(b->Binrd);
		vec_free(b->Boutrd);

		/* calculate and create new vectors */
		rdelem(&(b->Bgen),&(b->Bkill),b->Belem);
		if (b->BC == BCasm)
		{   vec_clear(b->Bkill);	// KILL nothing
		    vec_set(b->Bgen);		// GEN everything
		}
		b->Binrd = vec_calloc(deftop);
		b->Boutrd = vec_calloc(deftop);
	}
}

/**********************
 * Compute # of definition elems (deftop).
 */

STATIC void numdefelems(register elem *n)
{
  while (1)
  {	assert(n);
	if (OTdef(n->Eoper))
		deftop++;
	if (OTbinary(n->Eoper))
	{
		numdefelems(n->E1);
		n = n->E2;
	}
	else if (OTunary(n->Eoper))
	{
		n = n->E1;
	}
	else
		break;
  }
}

/**************************
 * Load defnod[] array.
 * Loaded in order of execution of the elems. Not sure if this is
 * necessary.
 */

STATIC void asgdefelems(block *b,register elem *n)
{	register unsigned op;

	assert(b && n);
	op = n->Eoper;
	if (ERTOL(n))
	{	asgdefelems(b,n->E2);
		asgdefelems(b,n->E1);
	}
	else if (OTbinary(op))
	{	asgdefelems(b,n->E1);
		asgdefelems(b,n->E2);
	}
	else if (OTunary(op))
		asgdefelems(b,n->E1);
	if (OTdef(op))
	{	assert(defnod);
		defnod[deftop].DNblock = b;
		defnod[deftop].DNelem = n;
		deftop++;
	}
}

/*************************************
 * Allocate and compute rd GEN and KILL.
 */

STATIC void rdelem(vec_t *pgen,vec_t *pkill,			/* where to put result		*/
	elem *n )				/* tree to evaluate for GEN and KILL */
{
	*pgen = vec_calloc(deftop);
	*pkill = vec_calloc(deftop);
	if (n)
		accumrd(*pgen,*pkill,n);
}

/**************************************
 * Accumulate GEN and KILL vectors for this elem.
 */

STATIC void accumrd(vec_t GEN,vec_t KILL,elem *n)
{	vec_t Gl,Kl,Gr,Kr;
	unsigned op,i;

	assert(GEN && KILL && n);
	op = n->Eoper;
	if (OTunary(op))
		accumrd(GEN,KILL,n->E1);
	else if (OTbinary(op))
	{
	    if (op == OPcolon || op == OPcolon2)
	    {
		rdelem(&Gl,&Kl,n->E1);
		rdelem(&Gr,&Kr,n->E2);

		/* GEN = (GEN - Kl) | Gl |	*/
		/*      (GEN - Kr) | Gr		*/
		/* KILL |= Kl & Kr		*/

		vec_orass(Gl,Gr);
		vec_sub(Gr,GEN,Kl);
		vec_orass(Gl,Gr);
		vec_sub(Gr,GEN,Kr);
		vec_or(GEN,Gl,Gr);

		vec_andass(Kl,Kr);
		vec_orass(KILL,Kl);

		vec_free(Gl);
		vec_free(Kl);
		vec_free(Gr);
		vec_free(Kr);
	    }
	    else if (op == OPandand || op == OPoror)
	    {
		accumrd(GEN,KILL,n->E1);
		rdelem(&Gr,&Kr,n->E2);
		vec_orass(GEN,Gr);	/* GEN |= Gr			*/

		vec_free(Gr);
		vec_free(Kr);
	    }
	    else if (OTrtol(op) && ERTOL(n))
	    {	accumrd(GEN,KILL,n->E2);
		accumrd(GEN,KILL,n->E1);
	    }
	    else
	    {	accumrd(GEN,KILL,n->E1);
		accumrd(GEN,KILL,n->E2);
	    }
	}

	if (OTdef(op))			/* if definition elem		*/
		updaterd(n,GEN,KILL);
}

/******************** AVAILABLE EXPRESSIONS ***********************/

/************************************
 * Compute available expressions (AEs).
 * That is, expressions whose result is still current.
 * Bin = the set of AEs reaching the beginning of B.
 * Bout = the set of AEs reaching the end of B.
 */

void flowae()
{
  flowxx = AE;
  flowaecp();
}

/**************************** COPY PROPAGATION ************************/

/***************************************
 * Compute copy propagation info (CPs).
 * Very similar to AEs (the same code is used).
 * Using RDs for copy propagation is WRONG!
 * That is, set of copy statements still valid.
 * Bin = the set of CPs reaching the beginning of B.
 * Bout = the set of CPs reaching the end of B.
 */

void flowcp()
{
  flowxx = CP;
  flowaecp();
}

/*****************************************
 * Common flow analysis routines for Available Expressions and
 * Copy Propagation.
 * Input:
 *	flowxx
 */

STATIC void flowaecp()
{	vec_t tmp;
	register unsigned i;
	bool anychng;

	aecpgenkill();		/* Compute Bgen and Bkill for AEs or CPs */
	if (exptop <= 1)	/* if no expressions			*/
		return;

	/* The transfer equation is:			*/
	/*	Bin = & Bout(all predecessors P of B)	*/
	/*	Bout = (Bin - Bkill) | Bgen		*/
	/* Using Ullman's algorithm:			*/

	vec_clear(startblock->Bin);
	vec_copy(startblock->Bout,startblock->Bgen); /* these never change */
	if (startblock->BC == BCiftrue)
	    vec_copy(startblock->Bout2,startblock->Bgen2); // these never change

	/* For all blocks except startblock	*/
	for (i = 1; i < dfotop; i++)
	{	register block *b = dfo[i];

		vec_set(b->Bin);	/* Bin = all expressions	*/

		/* Bout = (Bin - Bkill) | Bgen	*/
		vec_sub(b->Bout,b->Bin,b->Bkill);
		vec_orass(b->Bout,b->Bgen);
		if (b->BC == BCiftrue)
		{   vec_sub(b->Bout2,b->Bin,b->Bkill2);
		    vec_orass(b->Bout2,b->Bgen2);
		}
	}

	tmp = vec_calloc(exptop);
	do
	{   anychng = FALSE;

	    // For all blocks except startblock
	    for (i = 1; i < dfotop; i++)
	    {   block *b = dfo[i];
		list_t bl = b->Bpred;
		block *bp;

		// Bin = & of Bout of all predecessors
		// Bout = (Bin - Bkill) | Bgen

		assert(bl);	// it must have predecessors
		bp = list_block(bl);
		if (bp->BC == BCiftrue && list_block(bp->Bsucc) != b)
		    vec_copy(b->Bin,bp->Bout2);
		else
		    vec_copy(b->Bin,bp->Bout);
		while (TRUE)
		{   bl = list_next(bl);
		    if (!bl)
			break;
		    bp = list_block(bl);
		    if (bp->BC == BCiftrue && list_block(bp->Bsucc) != b)
			vec_andass(b->Bin,bp->Bout2);
		    else
			vec_andass(b->Bin,bp->Bout);
		}

		if (anychng)
		{   vec_sub(b->Bout,b->Bin,b->Bkill);
		    vec_orass(b->Bout,b->Bgen);
		}
		else
		{   vec_sub(tmp,b->Bin,b->Bkill);
		    vec_orass(tmp,b->Bgen);
		    if (!vec_equal(tmp,b->Bout))
		    {   // Swap Bout and tmp instead of
			// copying tmp over Bout
			vec_t v;

			v = tmp;
			tmp = b->Bout;
			b->Bout = v;
			anychng = TRUE;
		    }
		}

		if (b->BC == BCiftrue)
		{   // Bout2 = (Bin - Bkill2) | Bgen2
		    if (anychng)
		    {   vec_sub(b->Bout2,b->Bin,b->Bkill2);
			vec_orass(b->Bout2,b->Bgen2);
		    }
		    else
		    {   vec_sub(tmp,b->Bin,b->Bkill2);
			vec_orass(tmp,b->Bgen2);
			if (!vec_equal(tmp,b->Bout2))
			{   // Swap Bout and tmp instead of
			    // copying tmp over Bout2
			    vec_t v;

			    v = tmp;
			    tmp = b->Bout2;
			    b->Bout2 = v;
			    anychng = TRUE;
			}
		    }
		}
	    }
	} while (anychng);
	vec_free(tmp);
}

/******************************
 * A variable to avoid parameter overhead to asgexpelems().
 */

static block *this_block;

/***********************************
 * Compute Bgen and Bkill for AEs, CPs, and VBEs.
 */

STATIC void aecpgenkill()
{	register unsigned i;
	unsigned exptopsave;

#if TX86
	util_free(expnod);		/* dump any existing one	*/
#elif !(TARGET_MAC)	/* might be from previous memory pool */
	MEM_BEF_FREE(expnod);		/* dump any existing one	*/
#endif
	expnod = NULL;

	/* Compute number of expressions */
	exptop = 1;			/* start at 1			*/
	for (i = 0; i < dfotop; i++)
		if (dfo[i]->Belem)
		{	if (flowxx == CP)
				numcpelems(dfo[i]->Belem);
			else // AE || VBE
				numaeelems(dfo[i]->Belem);
		}
	if (exptop <= 1)		/* if no expressions		*/
		return;

	/* Allocate array of pointers to all expression elems.		*/
	/* (The elems are in order. Also, these expressions must not	*/
	/* have any side effects, and possibly should not be machine	*/
	/* dependent primitive addressing modes.)			*/
#if TX86
	expnod = (elem **) util_calloc(sizeof(elem *),exptop);
	util_free(expblk);
	expblk = (flowxx == VBE)
		? (block **) util_calloc(sizeof(block *),exptop) : NULL;
#else
	expnod = (elem **) MEM_BEF_CALLOC(sizeof(elem *) * exptop);
#if !(TARGET_MAC)	/* might be from previous memory pool */
	MEM_BEF_FREE(expblk);
#endif
	expblk = (flowxx == VBE)
		? (block **) MEM_BEF_CALLOC(sizeof(block *) * exptop) : NULL;
#endif

	exptopsave = exptop;
	exptop = 1;
	for (i = 0; i < dfotop; i++)
	{	this_block = dfo[i];	/* so asgexpelems knows about this */
		if (this_block->Belem)
			asgexpelems(this_block->Belem);
	}
	assert(exptop == exptopsave);

	defstarkill();			/* compute defkill and starkill	*/

#if 0
	assert(vec_numbits(defkill) == exptop);
	assert(vec_numbits(starkill) == exptop);
	assert(vec_numbits(vptrkill) == exptop);
	dbg_printf("defkill  "); vec_println(defkill);
	if (starkill)
	    {   dbg_printf("starkill "); vec_println(starkill);}
	if (vptrkill)
	    {   dbg_printf("vptrkill "); vec_println(vptrkill); }
#endif

	for (i = 0; i < dfotop; i++)	/* for each block		*/
	{	register block *b = dfo[i];
		elem *e;

		/* dump any existing vectors	*/
		vec_free(b->Bin);
		vec_free(b->Bout);
		vec_free(b->Bgen);
		vec_free(b->Bkill);
		b->Bgen = vec_calloc(exptop);
		b->Bkill = vec_calloc(exptop);
		switch (b->BC)
		{
		    case BCiftrue:
			vec_free(b->Bout2);
			vec_free(b->Bgen2);
			vec_free(b->Bkill2);
			for (e = b->Belem; e->Eoper == OPcomma; e = e->E2)
			    accumaecp(b->Bgen,b->Bkill,e);
			if (e->Eoper == OPandand || e->Eoper == OPoror)
			{   vec_t Kr,Gr;

			    accumaecp(b->Bgen,b->Bkill,e->E1);
			    Kr = vec_calloc(exptop);
			    Gr = vec_calloc(exptop);
			    accumaecp(Gr,Kr,e->E2);

			    // We might or might not have executed E2
			    // KILL1 = KILL | Kr
			    // GEN1 = GEN & ((GEN - Kr) | Gr)

			    // We definitely executed E2
			    // KILL2 = (KILL - Gr) | Kr
			    // GEN2 = (GEN - Kr) | Gr

			    unsigned j,dim;
			    dim = vec_dim(Kr);
			    vec_t KILL = b->Bkill;
			    vec_t GEN = b->Bgen;

			    for (j = 0; j < dim; j++)
			    {   vec_base_t KILL1,KILL2,GEN1,GEN2;

				KILL1 = KILL[j] | Kr[j];
				GEN1  = GEN[j] & ((GEN[j] & ~Kr[j]) | Gr[j]);

				KILL2 = (KILL[j] & ~Gr[j]) | Kr[j];
				GEN2  = (GEN[j] & ~Kr[j]) | Gr[j];

				KILL[j] = KILL1;
				GEN[j] = GEN1;
				Kr[j] = KILL2;
				Gr[j] = GEN2;
			    }

			    if (e->Eoper == OPandand)
			    {	b->Bkill  = Kr;
				b->Bgen   = Gr;
				b->Bkill2 = KILL;
				b->Bgen2  = GEN;
			    }
			    else
			    {	b->Bkill  = KILL;
				b->Bgen   = GEN;
				b->Bkill2 = Kr;
				b->Bgen2  = Gr;
			    }
			}
			else
			{
			    accumaecp(b->Bgen,b->Bkill,e);
			    b->Bgen2 = vec_clone(b->Bgen);
			    b->Bkill2 = vec_clone(b->Bkill);
			}
			b->Bout2 = vec_calloc(exptop);
			break;

		    case BCasm:
			vec_set(b->Bkill);		// KILL everything
			vec_clear(b->Bgen);		// GEN nothing
			break;

		    default:
			// calculate GEN & KILL vectors
			if (b->Belem)
			    accumaecp(b->Bgen,b->Bkill,b->Belem);
			break;
		}
#if 0
		dbg_printf("block %d Bgen ",i); vec_println(b->Bgen);
		dbg_printf("       Bkill "); vec_println(b->Bkill);
#endif
		b->Bin = vec_calloc(exptop);
		b->Bout = vec_calloc(exptop);
	}
}

/*****************************
 * Accumulate number of expressions in exptop.
 * Set NFLaecp as a flag indicating an AE elem.
 * Returns:
 *	TRUE if this elem is a possible AE elem.
 */

STATIC int numaeelems(register elem *n)
{ register unsigned op;
  unsigned ae;

  assert(n);
  op = n->Eoper;
  if (OTunary(op))
  {	ae = numaeelems(n->E1);
	// Disallow starred references to avoid problems with VBE's
	// being hoisted before tests of an invalid pointer.
	if (flowxx == VBE && op == OPind)
	    goto L1;
  }
  else if (OTbinary(op))
	ae = numaeelems(n->E1) & numaeelems(n->E2);
  else
	ae = TRUE;

  if (ae && OTae(op) && !(n->Ety & mTYvolatile) &&
      // Disallow struct AEs, because we can't handle CSEs that are structs
      tybasic(n->Ety) != TYstruct)
  {	n->Nflags |= NFLaecp;		/* remember for asgexpelems()	*/
	exptop++;
  }
  else
  L1:
	n->Nflags &= ~NFLaecp;
  return n->Nflags & NFLaecp;
}


/****************************
 * Compute number of cp elems into exptop.
 * Mark cp elems by setting NFLaecp flag.
 */

STATIC void numcpelems(elem *n)
{ unsigned op;

  op = n->Eoper;
  if (OTunary(op))
	numcpelems(n->E1);
  else if (OTbinary(op))
  {	numcpelems(n->E1);
	numcpelems(n->E2);

	/* look for elem of the form OPvar=OPvar, where they aren't the	*/
	/* same variable.						*/
	if (op == OPeq &&
	    n->E1->Eoper == OPvar &&
	    n->E2->Eoper == OPvar &&
	    !((n->E1->Ety | n->E2->Ety) & mTYvolatile) &&
	    n->E1->EV.sp.Vsym != n->E2->EV.sp.Vsym)
	{	exptop++;
		n->Nflags |= NFLaecp;
		return;
	}
  }
  n->Nflags &= ~NFLaecp;
}

/********************************
 * Assign ae (or cp) elems to expnod[] (in order of evaluation).
 */

STATIC void asgexpelems(elem *n)
{
  assert(n);
  if (OTunary(n->Eoper))
	asgexpelems(n->E1);
  else if (ERTOL(n))
  {	asgexpelems(n->E2);
	asgexpelems(n->E1);
  }
  else if (OTbinary(n->Eoper))
  {	asgexpelems(n->E1);
	asgexpelems(n->E2);
  }

  if (n->Nflags & NFLaecp)		/* if an ae, cp or vbe elem	*/
  {	n->Eexp = exptop;		/* remember index into expnod[]	*/
	expnod[exptop] = n;
	if (expblk)
		expblk[exptop] = this_block;
	exptop++;
  }
  else
	n->Eexp = 0;
}

/********************************
 * Compute defkill, starkill and vptrkill vectors.
 *	starkill:	set of expressions killed when a variable is
 *			changed that somebody could be pointing to.
 *			(not needed for cp)
 *			starkill is a subset of defkill.
 *	defkill:	set of expressions killed by an ambiguous
 *			definition.
 *	vptrkill:	set of expressions killed by an access to a vptr.
 */

STATIC void defstarkill()
{	register unsigned i,op;
	register elem *n;

	vec_free(vptrkill);
	vec_free(defkill);
	vec_free(starkill);		/* dump any existing ones	*/
	defkill = vec_calloc(exptop);
	if (flowxx != CP)
	{   starkill = vec_calloc(exptop);	/* and create new ones	*/
	    vptrkill = vec_calloc(exptop);	/* and create new ones	*/
	}
	else /* CP */
	{   starkill = NULL;
	    vptrkill = NULL;
	}

	if (flowxx == CP)
	{
	    for (i = 1; i < exptop; i++)
	    {   n = expnod[i];
		op = n->Eoper;
		assert(op == OPeq);
		assert(n->E1->Eoper==OPvar && n->E2->Eoper==OPvar);

		// Set bit in defkill if either the left or the
		// right variable is killed by an ambiguous def.

		Symbol *s1 = n->E1->EV.sp.Vsym;
		if (!(s1->Sflags & SFLunambig) ||
		    !(n->E2->EV.sp.Vsym->Sflags & SFLunambig))
		{
		    vec_setbit(i,defkill);
		}
	    }
	}
	else
	{
	    for (i = 1; i < exptop; i++)
	    {   n = expnod[i];
		op = n->Eoper;
		switch (op)
		{
		    case OPvar:
			if (!(n->EV.sp.Vsym->Sflags & SFLunambig))
			    vec_setbit(i,defkill);
			break;

		    case OPind:		// if a 'starred' ref

#if 1
/* The following program fails for this:
import std.c.stdio;

class Foo
{
    string foo = "abc";
    size_t i = 0;

    void bar()
    {
	printf("%c\n", foo[i]);
	i++;
	printf("%c\n", foo[i]);
    }
}

void main()
{
    auto f = new Foo();
    f.bar();
}
*/
			// For C/C++, casting to 'const' doesn't mean it
			// actually is const,
			// but immutable really doesn't change
			if ((n->Ety & (mTYimmutable | mTYvolatile)) == mTYimmutable &&
			    n->E1->Eoper == OPvar &&
			    n->E1->EV.sp.Vsym->Sflags & SFLunambig
			   )
			    break;
#endif
#if TX86
		    case OPstrlen:
		    case OPstrcmp:
		    case OPmemcmp:
		    case OPbt:		// OPbt is like OPind
#endif
			vec_setbit(i,defkill);
			vec_setbit(i,starkill);
			break;


		    case OPvptrfptr:
		    case OPcvptrfptr:
			vec_setbit(i,vptrkill);
#if TARGET_MAC		/* implied 'starred' ref */
			vec_setbit(i,defkill);
			vec_setbit(i,starkill);
			break;
#endif
			goto Lunary;

		    default:
			if (OTunary(op))
			{
			Lunary:
			    if (vec_testbit(n->E1->Eexp,defkill))
				    vec_setbit(i,defkill);
			    if (vec_testbit(n->E1->Eexp,starkill))
				    vec_setbit(i,starkill);
			}
			else if (OTbinary(op))
			{
			    if (vec_testbit(n->E1->Eexp,defkill) ||
				vec_testbit(n->E2->Eexp,defkill))
				    vec_setbit(i,defkill);
			    if (vec_testbit(n->E1->Eexp,starkill) ||
				vec_testbit(n->E2->Eexp,starkill))
				    vec_setbit(i,starkill);
			}
			break;
		}
	    }
	}
}

/********************************
 * Compute GEN and KILL vectors only for AEs.
 * defkill and starkill are assumed to be already set up correctly.
 * expnod[] is assumed to be set up correctly.
 */

void genkillae()
{	register unsigned i;

	flowxx = AE;
	assert(exptop > 1);
	for (i = 0; i < dfotop; i++)
	{	register block *b = dfo[i];

		assert(b);
		vec_clear(b->Bgen);
		vec_clear(b->Bkill);
		if (b->Belem)
		    accumaecp(b->Bgen,b->Bkill,b->Belem);
		else if (b->BC == BCasm)
		{   vec_set(b->Bkill);		// KILL everything
		    vec_clear(b->Bgen);		// GEN nothing
		}
	}
}

/************************************
 * Allocate and compute KILL and GEN vectors for a elem.
 */

STATIC void aecpelem(vec_t *pgen,vec_t *pkill, elem *n)
{	*pgen = vec_calloc(exptop);
	*pkill = vec_calloc(exptop);
	if (n)
	{	if (flowxx == VBE)
			accumvbe(*pgen,*pkill,n);
		else
			accumaecp(*pgen,*pkill,n);
	}
}

/*************************************
 * Accumulate GEN and KILL sets for AEs and CPs for this elem.
 */

static vec_t GEN;	// use static copies to save on parameter passing
static vec_t KILL;

STATIC void accumaecp(vec_t g,vec_t k,elem *n)
{   vec_t GENsave,KILLsave;

    assert(g && k);
    GENsave = GEN;
    KILLsave = KILL;
    GEN = g;
    KILL = k;
    accumaecpx(n);
    GEN = GENsave;
    KILL = KILLsave;
}

STATIC void accumaecpx(elem *n)
{   unsigned i,op;
    elem *t;

    assert(n);
    elem_debug(n);
    op = n->Eoper;

    switch (op)
    {
	case OPvar:
	case OPconst:
	case OPrelconst:
	    if ((flowxx == AE) && n->Eexp)
	    {	unsigned b;
#ifdef DEBUG
		assert(expnod[n->Eexp] == n);
#endif
		b = n->Eexp;
		vec_setclear(b,GEN,KILL);
	    }
	    return;
	case OPcolon:
	case OPcolon2:
	{   vec_t Gl,Kl,Gr,Kr;

	    aecpelem(&Gl,&Kl,n->E1);
	    aecpelem(&Gr,&Kr,n->E2);

	    /* KILL |= Kl | Kr		 */
	    /* GEN =((GEN - Kl) | Gl) &	 */
	    /*     ((GEN - Kr) | Gr)	 */

	    vec_orass(KILL,Kl);
	    vec_orass(KILL,Kr);

	    vec_sub(Kl,GEN,Kl);
	    vec_sub(Kr,GEN,Kr);
	    vec_orass(Kl,Gl);
	    vec_orass(Kr,Gr);
	    vec_and(GEN,Kl,Kr);

	    vec_free(Gl);
	    vec_free(Gr);
	    vec_free(Kl);
	    vec_free(Kr);
	    break;
	}
	case OPandand:
	case OPoror:
	{   vec_t Gr,Kr;

	    accumaecpx(n->E1);
	    aecpelem(&Gr,&Kr,n->E2);

	    if (!el_noreturn(n->E2))
	    {
		// KILL |= Kr
		// GEN &= (GEN - Kr) | Gr

		vec_orass(KILL,Kr);
		vec_sub(Kr,GEN,Kr);
		vec_orass(Kr,Gr);
		vec_andass(GEN,Kr);
	    }

	    vec_free(Gr);
	    vec_free(Kr);
	    break;
	}
	case OPasm:
	    assert(!n->Eexp);			// no ASM available expressions
	    vec_set(KILL);			// KILL everything
	    vec_clear(GEN);			// GEN nothing
	    return;

#if TX86
	case OPeq:
	case OPstreq:
	    accumaecpx(n->E2);
	case OPnegass:
	    accumaecpx(n->E1);
	    t = Elvalue(n);
	    break;
#endif

	case OPvptrfptr:
	case OPcvptrfptr:			// if vptr access
	    if ((flowxx == AE) && n->Eexp)
		vec_orass(KILL,vptrkill);	// kill all other vptr accesses
	    break;

	default:
	    if (OTunary(op))
	    {
#if TX86
	case OPind:				// most common unary operator
		accumaecpx(n->E1);
#ifdef DEBUG
		assert(!OTassign(op));
#endif
#else
		accumaecpx(n->E1);
		if (OTassign(op))		// if assignment operator
		    t = Elvalue(n);
#endif
	    }
	    else if (OTbinary(op))
	    {
		if (OTrtol(op) && ERTOL(n))
		{   accumaecpx(n->E2);
		    accumaecpx(n->E1);
		}
		else
		{   accumaecpx(n->E1);
		    accumaecpx(n->E2);
		}
		if (OTassign(op))		// if assignment operator
		    t = Elvalue(n);
	    }
	    break;
    }


    /* Do copy propagation stuff first	*/

    if (flowxx == CP)
    {
	if (!OTdef(op))				/* if not def elem	*/
		return;
	if (!Eunambig(n))			/* if ambiguous def elem */
	{   vec_orass(KILL,defkill);
	    vec_subass(GEN,defkill);
	}
	else					/* unambiguous def elem	*/
	{   symbol *s;

	    assert(t->Eoper == OPvar);
	    s = t->EV.sp.Vsym;			// ptr to var being def'd
	    for (i = 1; i < exptop; i++)	/* for each ae elem	 */
	    {	register elem *e = expnod[i];

		/* If it could be changed by the definition,	 */
		/* set bit in KILL.				 */

		if (e->E1->EV.sp.Vsym == s || e->E2->EV.sp.Vsym == s)
		    vec_setclear(i,KILL,GEN);
	    }
	}

	/* GEN CP elems	*/
	if (n->Eexp)
	{   unsigned b = n->Eexp;

	    vec_setclear(b,GEN,KILL);
	}

	return;
    }

    /* Else Available Expression stuff	*/

    if (n->Eexp)
    {	unsigned b = n->Eexp;			// add elem to GEN

	assert(expnod[b] == n);
	vec_setclear(b,GEN,KILL);
    }
    else if (OTdef(op))				/* else if definition elem */
    {
	if (!Eunambig(n))			/* if ambiguous def elem */
	{   vec_orass(KILL,defkill);
	    vec_subass(GEN,defkill);
	    if (OTcalldef(op))
	    {	vec_orass(KILL,vptrkill);
		vec_subass(GEN,vptrkill);
	    }
	}
	else					/* unambiguous def elem	*/
	{   symbol *s;

	    assert(t->Eoper == OPvar);
	    s = t->EV.sp.Vsym;			/* idx of var being def'd */
	    if (!(s->Sflags & SFLunambig))
	    {	vec_orass(KILL,starkill);	/* kill all 'starred' refs */
		vec_subass(GEN,starkill);
	    }
	    for (i = 1; i < exptop; i++)	/* for each ae elem	 */
	    {	elem *e = expnod[i];
		int eop = e->Eoper;

		/* If it could be changed by the definition,	 */
		/* set bit in KILL.				 */
		if (eop == OPvar)
		{   if (e->EV.sp.Vsym != s)
			continue;
		}
		else if (OTunary(eop))
		{   if (!vec_testbit(e->E1->Eexp,KILL))
			continue;
		}
		else if (OTbinary(eop))
		{   if (!vec_testbit(e->E1->Eexp,KILL) &&
			!vec_testbit(e->E2->Eexp,KILL))
			continue;
		}
		else
			continue;

		vec_setclear(i,KILL,GEN);
	    }
	}

	/* GEN the lvalue of an assignment operator	 */
	if (OTassign(op) && !OTpost(op) && t->Eexp)
	{   unsigned b = t->Eexp;

	    vec_setclear(b,GEN,KILL);
	}
    }
}

/************************* LIVE VARIABLES **********************/

/*********************************
 * Do live variable analysis (LVs).
 * A variable is 'live' at some point if there is a
 * subsequent use of it before a redefinition.
 * Binlv = the set of variables live at the beginning of B.
 * Boutlv = the set of variables live at the end of B.
 * Bgen = set of variables used before any definition in B.
 * Bkill = set of variables unambiguously defined before
 *	 any use in B.
 * Note that Bgen & Bkill = 0.
 */

void flowlv()
{	vec_t tmp,livexit;
	register unsigned i;
	bool anychng;
	unsigned cnt;

	lvgenkill();		/* compute Bgen and Bkill for LVs.	*/
	//assert(globsym.top);	/* should be at least some symbols	*/

	/* Create a vector of all the variables that are live on exit	*/
	/* from the function.						*/

	livexit = vec_calloc(globsym.top);
	for (i = 0; i < globsym.top; i++)
	{	if (globsym.tab[i]->Sflags & SFLlivexit)
			vec_setbit(i,livexit);
	}

	/* The transfer equation is:				*/
	/*	Bin = (Bout - Bkill) | Bgen			*/
	/*	Bout = union of Bin of all successors to B.	*/
	/* Using Ullman's algorithm:				*/

	for (i = 0; i < dfotop; i++)		/* for each block B	*/
	{
		vec_copy(dfo[i]->Binlv,dfo[i]->Bgen);	/* Binlv = Bgen	*/
	}

	tmp = vec_calloc(globsym.top);
	cnt = 0;
	do
	{	anychng = FALSE;

		/* For each block B in reverse DFO order	*/
		for (i = dfotop; i--;)
		{	register block *b = dfo[i];
			register list_t bl = b->Bsucc;

			/* Bout = union of Bins of all successors to B.	*/
			if (bl)
			{	vec_copy(b->Boutlv,list_block(bl)->Binlv);
				while ((bl = list_next(bl)) != NULL)
				{   vec_orass(b->Boutlv,list_block(bl)->Binlv);
				}
			}
			else /* no successors, Boutlv = livexit */
			{   //assert(b->BC==BCret||b->BC==BCretexp||b->BC==BCexit);
			    vec_copy(b->Boutlv,livexit);
			}

			/* Bin = (Bout - Bkill) | Bgen			*/
			vec_sub(tmp,b->Boutlv,b->Bkill);
			vec_orass(tmp,b->Bgen);
			if (!anychng)
				anychng = !vec_equal(tmp,b->Binlv);
			vec_copy(b->Binlv,tmp);
		}
		cnt++;
		assert(cnt < 50);
	} while (anychng);
	vec_free(tmp);
	vec_free(livexit);
#if 0
	dbg_printf("Live variables\n");
	for (i = 0; i < dfotop; i++)
	{	dbg_printf("B%d IN\t",i);
		vec_println(dfo[i]->Binlv);
		dbg_printf("B%d GEN\t",i);
		vec_println(dfo[i]->Bgen);
		dbg_printf("  KILL\t");
		vec_println(dfo[i]->Bkill);
		dbg_printf("  OUT\t");
		vec_println(dfo[i]->Boutlv);
	}
#endif
}

/***********************************
 * Compute Bgen and Bkill for LVs.
 * Allocate Binlv and Boutlv vectors.
 */

STATIC void lvgenkill()
{	unsigned i;

	/* Compute ambigsym, a vector of all variables that could be	*/
	/* referenced by a *e or a call.				*/

	assert(ambigsym == NULL);
	ambigsym = vec_calloc(globsym.top);
	for (i = 0; i < globsym.top; i++)
		if (!(globsym.tab[i]->Sflags & SFLunambig))
			vec_setbit(i,ambigsym);

	for (i = 0; i < dfotop; i++)
	{	block *b = dfo[i];

		vec_free(b->Bgen);
		vec_free(b->Bkill);
		lvelem(&(b->Bgen),&(b->Bkill),b->Belem);
		if (b->BC == BCasm)
		{   vec_set(b->Bgen);
		    vec_clear(b->Bkill);
		}

		vec_free(b->Binlv);
		vec_free(b->Boutlv);
		b->Binlv = vec_calloc(globsym.top);
		b->Boutlv = vec_calloc(globsym.top);
	}

	vec_free(ambigsym);		/* dump any existing one	*/
	ambigsym = NULL;
}

/*****************************
 * Allocate and compute KILL and GEN for live variables.
 */

STATIC void lvelem(vec_t *pgen,vec_t *pkill,elem *n)
{
	*pgen = vec_calloc(globsym.top);
	*pkill = vec_calloc(globsym.top);
	if (n && globsym.top)
		accumlv(*pgen,*pkill,n);
}

/**********************************************
 * Accumulate GEN and KILL sets for LVs for this elem.
 */

STATIC void accumlv(vec_t GEN,vec_t KILL,elem *n)
{   vec_t Gl,Kl,Gr,Kr;
    register unsigned op;
    elem *t;

    assert(GEN && KILL && n);

    while (1)
    {	elem_debug(n);
	op = n->Eoper;
	switch (op)
	{
	    case OPvar:
		if (symbol_isintab(n->EV.sp.Vsym))
		{   SYMIDX si = n->EV.sp.Vsym->Ssymnum;

		    assert((unsigned)si < globsym.top);
		    if (!vec_testbit(si,KILL))	// if not in KILL
			vec_setbit(si,GEN);	// put in GEN
		}
		break;

	    case OPcolon:
	    case OPcolon2:
		lvelem(&Gl,&Kl,n->E1);
		lvelem(&Gr,&Kr,n->E2);

		/* GEN |= (Gl | Gr) - KILL	*/
		/* KILL |= (Kl & Kr) - GEN	*/

		vec_orass(Gl,Gr);
		vec_subass(Gl,KILL);
		vec_orass(GEN,Gl);
		vec_andass(Kl,Kr);
		vec_subass(Kl,GEN);
		vec_orass(KILL,Kl);

		vec_free(Gl);
		vec_free(Gr);
		vec_free(Kl);
		vec_free(Kr);
		break;

	    case OPandand:
	    case OPoror:
		accumlv(GEN,KILL,n->E1);
		lvelem(&Gr,&Kr,n->E2);

		/* GEN |= Gr - KILL	*/
		/* KILL |= 0		*/

		vec_subass(Gr,KILL);
		vec_orass(GEN,Gr);

		vec_free(Gr);
		vec_free(Kr);
		break;

	    case OPasm:
		vec_set(GEN);		/* GEN everything not already KILLed */
		vec_subass(GEN,KILL);
		break;

	    case OPnewarray:
	    case OPmultinewarray:
		accumlv(GEN,KILL,n->E1);
		accumlv(GEN,KILL,n->E2);
		goto L1;

	    case OPcall:
	    case OPcallns:
#if TX86
	    case OPstrcpy:
	    case OPmemcpy:
	    case OPmemset:
#endif
#ifdef DEBUG
		assert(OTrtol(op));
#endif
		accumlv(GEN,KILL,n->E2);
		accumlv(GEN,KILL,n->E1);
		goto L1;

#if TX86
	    case OPstrcat:
#ifdef DEBUG
		assert(!OTrtol(op));
#endif
		accumlv(GEN,KILL,n->E1);
		accumlv(GEN,KILL,n->E2);
#endif
	    L1:
		vec_orass(GEN,ambigsym);
		vec_subass(GEN,KILL);
		break;

	    case OPeq:
		/* Avoid GENing the lvalue of an =	*/
		accumlv(GEN,KILL,n->E2);
		t = Elvalue(n);
		if (t->Eoper != OPvar)
			accumlv(GEN,KILL,t->E1);
		else /* unambiguous assignment */
		{   symbol *s;

		    s = t->EV.sp.Vsym;
		    symbol_debug(s);

		    /* if not GENed already, KILL it */
		    if (symbol_isintab(s) &&
			!vec_testbit(s->Ssymnum,GEN) &&
			/* assignments to aggregates are	*/
			/* not unambiguous			*/
			!tyaggregate(s->ty()) &&
			t->EV.sp.Voffset == 0 &&
			tysize(t->Ety) == tysize(s->ty())
		       )
		    {	assert((unsigned)s->Ssymnum < globsym.top);
			vec_setbit(s->Ssymnum,KILL);
		    }
		}
		break;

	    case OPind:
	    case OPucall:
	    case OPucallns:
	    case OPstrlen:
		accumlv(GEN,KILL,n->E1);

		/* If it was a *p elem, set bits in GEN for all symbols	*/
		/* it could have referenced, but only if bits in KILL	*/
		/* are not already set.					*/

		vec_orass(GEN,ambigsym);
		vec_subass(GEN,KILL);
		break;

	    default:
		if (OTunary(op))
		{   n = n->E1;
		    continue;
		}
		else if (OTrtol(op) && ERTOL(n))
		{
		    accumlv(GEN,KILL,n->E2);

		    /* Note that lvalues of op=,i++,i-- elems */
		    /* are GENed.				*/
		    n = n->E1;
		    continue;
		}
		else if (OTbinary(op))
		{
		    accumlv(GEN,KILL,n->E1);
		    n = n->E2;
		    continue;
		}
		break;
	}
	break;
    }
}

/********************* VERY BUSY EXPRESSIONS ********************/

/**********************************************
 * Compute very busy expressions(VBEs).
 * That is,expressions that are evaluated along
 * separate paths.
 * Bin = the set of VBEs at the beginning of B.
 * Bout = the set of VBEs at the end of B.
 * Bgen = set of expressions X+Y such that X+Y is
 *	evaluated before any def of X or Y.
 * Bkill = set of expressions X+Y such that X or Y could
 *	be defined before X+Y is computed.
 * Note that gen and kill are mutually exclusive.
 */

void flowvbe()
{	vec_t tmp;
	unsigned i;
	bool anychng;

	flowxx = VBE;
	aecpgenkill();		/* compute Bgen and Bkill for VBEs	*/
	if (exptop <= 1)	/* if no candidates for VBEs		*/
		return;

	/*for (i = 0; i < exptop; i++)
		dbg_printf("expnod[%d] = 0x%x\n",i,expnod[i]);*/

	/* The transfer equation is:			*/
	/*	Bout = & Bin(all successors S of B)	*/
	/*	Bin =(Bout - Bkill) | Bgen		*/
	/* Using Ullman's algorithm:			*/

	/*dbg_printf("defkill = "); vec_println(defkill);
	dbg_printf("starkill = "); vec_println(starkill);*/

	for (i = 0; i < dfotop; i++)
	{	block *b = dfo[i];

		/*dbg_printf("block 0x%x\n",b);
		dbg_printf("Bgen = "); vec_println(b->Bgen);
		dbg_printf("Bkill = "); vec_println(b->Bkill);*/

		if (b->BC == BCret || b->BC == BCretexp || b->BC == BCexit)
			vec_clear(b->Bout);
		else
			vec_set(b->Bout);

		/* Bin = (Bout - Bkill) | Bgen	*/
		vec_sub(b->Bin,b->Bout,b->Bkill);
		vec_orass(b->Bin,b->Bgen);
	}

	tmp = vec_calloc(exptop);
	do
	{	anychng = FALSE;

		/* for all blocks except return blocks in reverse dfo order */
		for (i = dfotop; i--;)
		{	block *b = dfo[i];
			list_t bl;

			if (b->BC == BCret || b->BC == BCretexp || b->BC == BCexit)
				continue;

			/* Bout = & of Bin of all successors */
			bl = b->Bsucc;
			assert(bl);	/* must have successors		*/
			vec_copy(b->Bout,list_block(bl)->Bin);
			while (TRUE)
			{   bl = list_next(bl);
			    if (!bl)
				break;
			    vec_andass(b->Bout,list_block(bl)->Bin);
			}

			/* Bin = (Bout - Bkill) | Bgen	*/
			vec_sub(tmp,b->Bout,b->Bkill);
			vec_orass(tmp,b->Bgen);
			if (!anychng)
				anychng = !vec_equal(tmp,b->Bin);
			vec_copy(b->Bin,tmp);
		}
	} while (anychng);	/* while any changes occurred to any Bin */
	vec_free(tmp);
}

/*************************************
 * Accumulate GEN and KILL sets for VBEs for this elem.
 */

STATIC void accumvbe(vec_t GEN,vec_t KILL,elem *n)
{	register unsigned op,i;
	register elem *t;

	assert(GEN && KILL && n);
	op = n->Eoper;

	switch (op)
	{
	    case OPcolon:
	    case OPcolon2:
	    {	vec_t Gl,Gr,Kl,Kr;

		aecpelem(&Gl,&Kl,n->E1);
		aecpelem(&Gr,&Kr,n->E2);

		/* GEN |=((Gr - Kl) | (Gl - Kr)) - KILL	*/
		vec_subass(Gr,Kl);
		vec_subass(Gl,Kr);
		vec_orass(Gr,Gl);
		vec_subass(Gr,KILL);
		vec_orass(GEN,Gr);

		/* KILL |=(Kl | Kr) - GEN	*/
		vec_orass(Kl,Kr);
		vec_subass(Kl,GEN);
		vec_orass(KILL,Kl);

		vec_free(Gl);
		vec_free(Kl);
		vec_free(Gr);
		vec_free(Kr);
		break;
	    }

	    case OPandand:
	    case OPoror:
		accumvbe(GEN,KILL,n->E1);
		/* WARNING: just so happens that it works this way.	*/
		/* Be careful about (b+c)||(b+c) being VBEs, only the	*/
		/* first should be GENed. Doing things this way instead	*/
		/* of (GEN |= Gr - KILL) and (KILL |= Kr - GEN) will	*/
		/* ensure this.						*/
		accumvbe(GEN,KILL,n->E2);
		break;

	    case OPnegass:
		t = n->E1;
		if (t->Eoper != OPvar)
		{   accumvbe(GEN,KILL,t->E1);
		    if (OTbinary(t->Eoper))
			accumvbe(GEN,KILL,t->E2);
		}
		break;

	    case OPnewarray:
	    case OPmultinewarray:
		accumvbe(GEN,KILL,n->E1);
		accumvbe(GEN,KILL,n->E2);
		break;

	    case OPcall:
	    case OPcallns:
		accumvbe(GEN,KILL,n->E2);
	    case OPucall:
	    case OPucallns:
		t = n->E1;
		// Do not VBE indirect function calls
		if (t->Eoper == OPind)
		    t = t->E1;
		accumvbe(GEN,KILL,t);
		break;

	    case OPasm:			// if the dreaded OPasm elem
		vec_set(KILL);		// KILL everything
		vec_subass(KILL,GEN);	// except for GENed stuff
		return;

	    default:
		if (OTunary(op))
		{
		    t = n->E1;
		    accumvbe(GEN,KILL,t);
		}
		else if (ERTOL(n))
		{   accumvbe(GEN,KILL,n->E2);
		    t = n->E1;
		    // do not GEN the lvalue of an assignment op
		    if (OTassign(op))
		    {   t = Elvalue(n);
			if (t->Eoper != OPvar)
			{   accumvbe(GEN,KILL,t->E1);
			    if (OTbinary(t->Eoper))
				accumvbe(GEN,KILL,t->E2);
			}
		    }
		    else
			accumvbe(GEN,KILL,t);
		}
		else if (OTbinary(op))
		{
			/* do not GEN the lvalue of an assignment op	*/
			if (OTassign(op))
			{   t = Elvalue(n);
			    if (t->Eoper != OPvar)
			    {   accumvbe(GEN,KILL,t->E1);
				if (OTbinary(t->Eoper))
				    accumvbe(GEN,KILL,t->E2);
			    }
			}
			else
			    accumvbe(GEN,KILL,n->E1);
			accumvbe(GEN,KILL,n->E2);
		}
		break;
	}

	if (n->Eexp)			/* if a vbe elem		*/
	{	int ne = n->Eexp;

		assert(expnod[ne] == n);
		if (!vec_testbit(ne,KILL))	/* if not already KILLed */
		{
			/* GEN this expression only if it hasn't	*/
			/* already been GENed in this block.		*/
			/* (Don't GEN common subexpressions.)		*/
			if (vec_testbit(ne,GEN))
				vec_clearbit(ne,GEN);
			else
			{   vec_setbit(ne,GEN);	/* GEN this expression	*/
			    /* GEN all identical expressions		*/
			    /* (operators only, as there is no point	*/
			    /* to hoisting out variables and constants)	*/
			    if (!OTleaf(op))
			    {	for (i = 1; i < exptop; i++)
				{	if (op == expnod[i]->Eoper &&
					    i != ne &&
					    el_match(n,expnod[i]))
					    {	vec_setbit(i,GEN);
						assert(!vec_testbit(i,KILL));
					    }
				}
			    }
			}
		}
		if (op == OPvptrfptr || op == OPcvptrfptr)
		{
		    vec_orass(KILL,vptrkill);	/* KILL all vptr accesses */
		    vec_subass(KILL,GEN);	/* except for GENed stuff */
		}
	}
	else if (OTdef(op))		/* if definition elem		*/
	{
		if (!Eunambig(n))	/* if ambiguous definition	*/
		{	vec_orass(KILL,defkill);
			if (OTcalldef(op))
			    vec_orass(KILL,vptrkill);
		}
		else			/* unambiguous definition	*/
		{   symbol *s;

		    assert(t->Eoper == OPvar);
		    s = t->EV.sp.Vsym;	// ptr to var being def'd
		    if (!(s->Sflags & SFLunambig))
			vec_orass(KILL,starkill);/* kill all 'starred' refs */
		    for (i = 1; i < exptop; i++)	/* for each vbe elem */
		    {	elem *e = expnod[i];
			unsigned eop = e->Eoper;

			/* If it could be changed by the definition,	 */
			/* set bit in KILL.				 */
			if (eop == OPvar)
			{   if (e->EV.sp.Vsym != s)
				continue;
			}
			else if (OTbinary(eop))
			{   if (!vec_testbit(e->E1->Eexp,KILL) &&
				!vec_testbit(e->E2->Eexp,KILL))
				continue;
			}
			else if (OTunary(eop))
			{   if (!vec_testbit(e->E1->Eexp,KILL))
				continue;
			}
			else /* OPconst or OPrelconst or OPstring or OPhstring */
				continue;

			vec_setbit(i,KILL);	// KILL it
		    } /* for */
		} /* if */
		vec_subass(KILL,GEN);
	} /* if */
}

#endif
